package com.gzsxy.esjy.redis.common;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * @author xiaolong
 * @version 1.0
 * @description: 用ThreadLocal线程对象存储某接口的代理类
 * @date 2021/10/19 11:21
 */
public class ThreadLocalProxyHolder {

    //ThreadLocal可以解决多线程访问共享变量出现并发问题
    private static final Map<Class<?>, ThreadLocal<?>> localMap = new HashMap<>();

    public ThreadLocalProxyHolder() {
    }


    /**
     * @param mapperInterface 接口
     * @return
     * @description: 获取某接口代理对象
     */
    public static <T> T getContextHolderProxy(Class<T> mapperInterface) {
        ClassLoader classLoader = mapperInterface.getClassLoader();
        //强转类型
        Class<?>[] interfaces = new Class[]{mapperInterface};
        //第一个参数 目标对象，需要实现targetInterface接口
        //第二个参数 需要创建代理的接口
        //第三个参数 InvocationHandler实现类
        return (T) Proxy.newProxyInstance(classLoader, interfaces, new ObjectFactoryDelegatingInvocationHandler(new GosuncnObjectFactory(mapperInterface)));

    }


    //将代理对象写入threadLocal对象中 map<接口，实现类对象>
    public static void set(Object value) {
        //获得这个对象所实现的所有接口
        Class<?>[] interfaces = value.getClass().getInterfaces();

        for (int i = 0; i < interfaces.length; i++) {
            ThreadLocal<Object> threadLocal = (ThreadLocal<Object>) localMap.get(interfaces[i]);
            if (threadLocal != null) {
                //存入threadLocal对象
                threadLocal.set(value);
                return;
            }
        }
    }


    /**
     * @param
     * @return
     * @description: 删除线程本地内存的本地变量
     */
    public static void remove() {
        Set<Map.Entry<Class<?>, ThreadLocal<?>>> entrySet = localMap.entrySet();
        Iterator<Map.Entry<Class<?>, ThreadLocal<?>>> var1 = entrySet.iterator();
        while (var1.hasNext()) {
            Map.Entry<Class<?>, ThreadLocal<?>> entry = var1.next();
            ((ThreadLocal) entry.getValue()).remove();
        }
    }

    /**
     * @param type 接口
     * @return
     * @description: 删除线程本地内存的本地变量
     */
    public static void remove(Class<?> type) {
        ThreadLocal<?> threadLocal = localMap.get(type);
        if (threadLocal != null) {
            threadLocal.remove();
        }
    }


    /**
     * @param value 实现类（代理对象类）
     * @return
     * @description: 删除线程本地内存的本地变量
     */
    public static void remove(Object value) {
        Class<?>[] interfaces = value.getClass().getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            ThreadLocal<Object> threadLocal = (ThreadLocal<Object>) localMap.get(interfaces[i]);
            if (threadLocal != null) {
                threadLocal.remove();
                return;
            }
        }
    }

    /**
     * @description: 用来获取代理对象类（即接口的实现类，实现动态代理-->动态获取代理对象）
     * type   就是接口类class
     * @return
     */
    private static class GosuncnObjectFactory<T> implements Serializable {
        private final Class<?> name;

        private GosuncnObjectFactory(Class<?> type) {
            this.name = type;
            ThreadLocalProxyHolder.localMap.put(this.name, new ThreadLocal());
        }

        //获取到接口的实现类，即获取代理对象类--因为之前在请求时被过滤器拦截进行将接口的实现类set进入threadLocal对象
        public T getObject() {
            ThreadLocal<T> threadLocal = (ThreadLocal<T>) ThreadLocalProxyHolder.localMap.get(this.name);
            return threadLocal.get();
        }


        @Override
        public String toString() {
            return "Current" + this.name.getSimpleName();
        }

        public Class<?> getClazz() {
            return this.name;
        }

    }


    // newProxyInstance会返回一个代理对象，当调用代理对象的任何方法的时候，会就被InvocationHandler接口的invoke方法处理
    private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {

        private final ThreadLocalProxyHolder.GosuncnObjectFactory<?> objectFactory;

        private ObjectFactoryDelegatingInvocationHandler(GosuncnObjectFactory<?> objectFactory) {
            this.objectFactory = objectFactory;
        }


        /**
         * @param proxy  调用该方法的代理实例 newProxyInstance会返回一个代理对象
         * @param method 调用的方法
         * @param args   包含的方法调用传递代理实例的参数值的对象的阵列，或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中，例如java.lang.Integer或java.lang.Boolean 。
         * @return
         * @description: 代理作用就是，对所有的方法进行统一的操作，比如计算每个方法的耗时，这里的实际意义是每次请求的请求头信息
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                if (method.getName().equals("toString")) {
                    return this.objectFactory.toString();
                } else if (method.getName().equals("hashCode")) {
                    return this.objectFactory.hashCode();
                } else if (method.getName().equals("getClass")) {
                    return this.objectFactory.getClazz();
                } else {
                    Object object = this.objectFactory.getObject();
                    return method.invoke(object, args);
                }
            } catch (InvocationTargetException e) {
                throw e.getTargetException();
            }
        }
    }
}
